namespace ScrumSprintMonitor.ServerAdapter.TFS.TemplateAdaptors
{
	#region #using Directives

	using System;
	using System.ComponentModel;
	using System.Diagnostics;

	using Infrastructure;

	using Isolation.TeamFoundationServer;

	#endregion

	[DebuggerDisplay("#{Id}, Type: {Type}, State: {State}, ChangedBy: {ChangedBy}", Name = "IWorkItemAdapter", Target = typeof(IWorkItemAdapter))]
	public abstract class CoreWorkItemAdapter : IWorkItemAdapter
	{
		private string assignedTo;
		private string changedBy;
		private DateTime changedDate;
		private double doneWork;
		private int id;
		private double remainingWork;
		private WorkItemState state;
		private string team;
		private string title;
		private WorkItemType type;
		private IWorkItem workItem;

		protected CoreWorkItemAdapter(IWorkItem workItem)
		{
			this.workItem = workItem;
		}

		protected IWorkItem WorkItem
		{
			[DebuggerStepThrough]
			get { return this.workItem; }
		}

		protected virtual void RetrieveTeam()
		{
		}

		protected abstract void RetrieveType();
		protected abstract void RetrieveState();
		protected abstract void RetrieveDoneWork();
		protected abstract void RetrieveRemainingWork();

		public override bool Equals(object obj)
		{
			var other = obj as IWorkItemAdapter;

			if (other == null)
			{
				return false;
			}

			if (other.ChangedDate != ChangedDate)
			{
				return false;
			}
			if (other.ChangedBy != ChangedBy)
			{
				return false;
			}
			if (other.Team != Team)
			{
				return false;
			}
			if (other.AssignedTo != AssignedTo)
			{
				return false;
			}
			if (other.Id != Id)
			{
				return false;
			}
			if (other.State != State)
			{
				return false;
			}
			if (other.Type != Type)
			{
				return false;
			}
			if (!Equals(other.DoneWork, DoneWork))
			{
				return false;
			}
			if (!Equals(other.RemainingWork, RemainingWork))
			{
				return false;
			}
			if (other.Title != Title)
			{
				return false;
			}

			return true;
		}

		public override int GetHashCode()
		{
			return (this.workItem != null ? this.workItem.GetHashCode() : 0);
		}

		#region Implementation of IWorkItemAdapter

		public int Id
		{
			[DebuggerStepThrough]
			get { return this.id; }
		}

		public WorkItemType Type
		{
			[DebuggerStepThrough]
			get { return this.type; }
			protected set
			{
				if (value != Type)
				{
					this.type = value;

					OnPropertyChanged("Type");
				}
			}
		}

		public WorkItemState State
		{
			[DebuggerStepThrough]
			get { return this.state; }
			protected set
			{
				if (value != this.state)
				{
					this.state = value;

					OnPropertyChanged("State");
				}
			}
		}

		public double RemainingWork
		{
			[DebuggerStepThrough]
			get { return this.remainingWork; }
			protected set
			{
				if (value != this.remainingWork)
				{
					this.remainingWork = value;

					OnPropertyChanged("RemainingWork");
				}
			}
		}

		public double DoneWork
		{
			[DebuggerStepThrough]
			get { return this.doneWork; }
			protected set
			{
				if (value != this.doneWork)
				{
					this.doneWork = value;

					OnPropertyChanged("DoneWork");
				}
			}
		}

		public string Title
		{
			[DebuggerStepThrough]
			get { return this.title; }
			private set
			{
				if (value != this.title)
				{
					this.title = value;

					OnPropertyChanged("Title");
				}
			}
		}

		public string Team
		{
			[DebuggerStepThrough]
			get { return this.team; }
			protected set
			{
				if (value != this.team)
				{
					this.team = value;

					OnPropertyChanged("Team");
				}
			}
		}

		public string AssignedTo
		{
			[DebuggerStepThrough]
			get { return this.assignedTo; }
			private set
			{
				if (value != this.assignedTo)
				{
					this.assignedTo = value;

					OnPropertyChanged("AssignedTo");
				}
			}
		}

		public string ChangedBy
		{
			[DebuggerStepThrough]
			get { return this.changedBy; }
			private set
			{
				if (value != this.changedBy)
				{
					this.changedBy = value;

					OnPropertyChanged("ChangedBy");
				}
			}
		}

		public DateTime ChangedDate
		{
			[DebuggerStepThrough]
			get { return this.changedDate; }
			private set
			{
				if (value != this.changedDate)
				{
					this.changedDate = value;

					OnPropertyChanged("ChangedDate");
				}
			}
		}

		public void Initialize()
		{
			try
			{
				this.id = this.workItem.Id;

				Title = this.workItem.Title;
				AssignedTo = this.workItem.AssignedTo;
				ChangedBy = this.workItem.ChangedBy;
				ChangedDate = this.workItem.ChangedDate;

				RetrieveTeam();
				RetrieveType();
				RetrieveState();
				RetrieveRemainingWork();
				RetrieveDoneWork();
			}
			catch (TfsConnectionException e)
			{
				throw new ConnectionException(e);
			}
		}

		public void UpdateFrom(IWorkItemAdapter sourceAdapter)
		{
			if (sourceAdapter.Id != Id)
			{
				throw new ArgumentException("The sourceAdapter does not refer to the same work item!", "sourceAdapter");
			}

			var sourceCoreWorkItemAdapter = (CoreWorkItemAdapter) sourceAdapter;

			this.workItem = sourceCoreWorkItemAdapter.WorkItem;

			Title = sourceCoreWorkItemAdapter.Title;
			Team = sourceCoreWorkItemAdapter.Team;
			AssignedTo = sourceCoreWorkItemAdapter.AssignedTo;
			ChangedDate = sourceCoreWorkItemAdapter.ChangedDate;
			ChangedBy = sourceCoreWorkItemAdapter.ChangedBy;
			DoneWork = sourceAdapter.DoneWork;
			RemainingWork = sourceAdapter.RemainingWork;
			State = sourceAdapter.State;
			Type = sourceAdapter.Type;
		}

		#endregion

		#region INotifyPropertyChanged members

		public event PropertyChangedEventHandler PropertyChanged;

		public void OnPropertyChanged(string propertyName)
		{
			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
			}
		}

		#endregion
	}
}